package cn.mw.cmdb.util;


import cn.hutool.core.bean.BeanDesc;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.PropDesc;
import cn.mw.cmdb.entity.*;
import cn.mw.cmdb.service.impl.CmdbServiceManage;
import cn.mw.cmdb.service.impl.UNumManageServiceImpl;
import cn.mw.cmdb.view.TreeViewObject;
import cn.mw.components.mongodb.anno.AutoId;
import cn.mw.components.mongodb.anno.CMDBProperty;
import cn.mw.components.mongodb.anno.CMDBRelations;
import cn.mw.components.mongodb.config.MongoConstant;
import cn.mw.components.mongodb.entity.GroupSearchOperation;
import cn.mw.components.mongodb.entity.SearchParam;
import cn.mw.components.mongodb.filter.DefaultFilter;
import cn.mw.components.mongodb.filter.FieldsFilter;
import cn.mw.components.mongodb.filter.GroupFilter;
import cn.mw.components.mongodb.mapper.AggregateQueryFilter;
import cn.mw.components.mongodb.mapper.BaseMapper;
import cn.mw.components.mongodb.mapper.QueryFilter;
import cn.mw.components.mongodb.page.MWIPage;
import cn.mw.components.mongodb.page.MWOrderItem;
import cn.mw.components.mongodb.page.MWPage;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.components.mongodb.tool.FieldGetter;
import cn.mw.components.mongodb.tool.MongoUtils;
import cn.mw.microMonitorCommon.api.cmdb.TreeView;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import cn.mw.microMonitorCommon.security.dto.MwUser;
import cn.mw.microMonitorCommon.security.utils.SecurityUtils;
import cn.mw.microMonitorCommon.util.RedisUtils;
import cn.mw.microMonitorCommon.util.SpringUtils;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.nacos.common.utils.CollectionUtils;
import com.alibaba.nacos.shaded.com.google.common.base.Strings;
import com.mongodb.client.result.UpdateResult;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
import org.springframework.data.mongodb.core.aggregation.AggregationResults;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;
import org.springframework.util.StringUtils;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

import static cn.mw.cmdb.config.MQTT.MessageArrivedHandle.onlineStateKey;
import static cn.mw.cmdb.config.MQTT.MessageArrivedHandle.redisUMangeKey;
import static cn.mw.cmdb.config.MQTT.OnlineStateEnum.offline;
import static cn.mw.cmdb.config.MQTT.OnlineStateEnum.online;
import static cn.mw.cmdb.entity.InstanceInfo.SearchPrefix;
import static cn.mw.cmdb.processor.MonitorValue.*;
import static cn.mw.cmdb.service.impl.InstanceServiceImpl.KEY_ID;
import static cn.mw.cmdb.util.BaseUtil.getNowLocalTime;
import static cn.mw.cmdb.util.ValConvertUtil.strValueConvert;
import static cn.mw.cmdb.viewProcessor.ModelViewCabinetProcess.cabinetCode;
import static cn.mw.cmdb.viewProcessor.ModelViewCabinetProcess.cabinetRuleType;

@Slf4j
public class CMDBTool {
    public static final String CMDBDefaultIdKey = "id";
    public static final String CMDBDefaultIdName = "ID";
    public static final List<String> encryptFieldList = Arrays.asList(CommunityKey, PriValueKey, AuthValueKey);

    private static int firstPageNo = 1;

    public static Document tranformFrom(Object object) {
        Document document = new Document();
        return document;
    }

    public static MWIPage extractPageInfo(Map params) {
        MWPage mwPage = null;
        Map map = (Map) params.get(MWIPage.Key);
        if (null != map) {
            mwPage = new MWPage();
            BeanUtil.fillBeanWithMap(map, mwPage, false);
        }
        return mwPage;
    }

    public static Update genMongoUpdateByMap(Map map, Update update, List<String> ignoreList) {
        Update newUpdate = update;
        if (null == update) {
            newUpdate = new Update();
        }
        List<String> fieldList = genInstanceField(InstanceInfo.class);
        if (!CollectionUtils.isMapEmpty(map)) {
            Update finalNewUpdate = newUpdate;
            Set<Map.Entry<Object, Object>> entrySet = map.entrySet();
            for (Map.Entry<Object, Object> ms : entrySet) {
                String key = strValueConvert(ms.getKey());
                if (ignoreList != null && ignoreList.contains(key)) {
                    continue;
                }
                if (fieldList.contains(key)) {
                    finalNewUpdate.set(key, ms.getValue());
                } else {
                    String keyField = PropertyType.formatSearchKey(InstanceInfo.SearchPrefix, key);
                    finalNewUpdate.set(keyField, ms.getValue());
                }
            }
        }
        return newUpdate;
    }


    /*
     * 产生mongodb更新语句, 如果CMDBModelProperty注解有id标识则不处理
     */
    public static Update genMongoUpdate(Object value, Predicate<PropDesc> predicate, Update update) {
        BeanDesc desc = BeanUtil.getBeanDesc(value.getClass());
        Iterator<PropDesc> ite = desc.getProps().iterator();
        Update newUpdate = update;
        if (null == update) {
            newUpdate = new Update();
        }

        int updCount = 0;
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            if (null != predicate && !predicate.test(propDesc)) {
                continue;
            }

            Annotation annotation = field.getAnnotation(CMDBProperty.class);
            AutoId autoId = field.getAnnotation(AutoId.class);
            if (null != annotation) {
                CMDBProperty cmdbModelProperty = (CMDBProperty) annotation;
                if (!cmdbModelProperty.id() && null == autoId) {
                    try {
                        Method getMethod = desc.getGetter(propDesc.getFieldName());
                        Object getValue = getMethod.invoke(value);
                        if (null != getValue && !SearchPrefix.equals(propDesc.getFieldName())) {
                            newUpdate.set(propDesc.getFieldName(), getValue);
                            updCount++;
                        }
                    } catch (Exception e) {
                        log.warn("field {} genUpdate error:{}", propDesc.getFieldName(), e.getMessage());
                    }
                }
            }
        }
        if (updCount == 0) {
            return null;
        }
        return newUpdate;
    }

    public static Update genMongoInstanceUpdate(Object value, String prefix) {
        BeanDesc desc = BeanUtil.getBeanDesc(value.getClass());
        Iterator<PropDesc> ite = desc.getProps().iterator();
        Update newUpdate = new Update();
        int updCount = 0;
        List<String> fieldList = genInstanceField(InstanceInfo.class);
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            Annotation annotation = field.getAnnotation(CMDBProperty.class);
            AutoId autoId = field.getAnnotation(AutoId.class);
            if (null != annotation) {
                CMDBProperty cmdbModelProperty = (CMDBProperty) annotation;
                if (!cmdbModelProperty.id() && null == autoId) {
                    try {
                        Method getMethod = desc.getGetter(propDesc.getFieldName());
                        Object getValue = getMethod.invoke(value);
                        if (null != getValue && CollectionUtils.isNotEmpty(fieldList)) {
                            if (fieldList.contains(propDesc.getFieldName())) {
                                newUpdate.set(propDesc.getFieldName(), getValue);
                                updCount++;
                            } else {
                                String name = PropertyType.formatSearchKey(prefix, propDesc.getFieldName());
                                newUpdate.set(name, getValue);
                                updCount++;
                            }
                        }
                    } catch (Exception e) {
                        log.warn("field {} genUpdate error:{}", propDesc.getFieldName(), e.getMessage());
                    }
                }
            }
        }
        if (updCount == 0) {
            return null;
        }
        return newUpdate;
    }


    /**
     * 获取instanceInfo实体更新字段, 不包含data字段
     * 只更新CMDBModelProperty有注解的字段，注解有id标识则不处理
     *
     * @return
     */
    public static List<String> genInstanceField(Class clazz) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        List<String> fieldList = new ArrayList<>();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            Annotation annotation = field.getAnnotation(CMDBProperty.class);
            AutoId autoId = field.getAnnotation(AutoId.class);
            if (null != annotation) {
                CMDBProperty cmdbModelProperty = (CMDBProperty) annotation;
                if (!cmdbModelProperty.id() && null == autoId) {
                    try {
                        if (!SearchPrefix.equals(propDesc.getFieldName())) {
                            fieldList.add(propDesc.getFieldName());
                        }
                    } catch (Exception e) {
                        log.warn("get instance field {} error:{}", propDesc.getFieldName(), e.getMessage());
                    }
                }
            }
        }
        return fieldList;
    }

    /**
     * @param clazz
     * @param fieldList     字段id
     * @param fieldNameList 字段名称
     */
    public static void getExcelExportFieldInfo(Class clazz, List<String> fieldList, List<String> fieldNameList) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            Annotation annotation = field.getAnnotation(ExcelProperty.class);
            if (null != annotation) {
                ExcelProperty excelProperty = (ExcelProperty) annotation;
                try {
                    String[] value = excelProperty.value();
                    String fieldName = value[0];
                    fieldNameList.add(fieldName);
                    fieldList.add(propDesc.getFieldName());
                } catch (Exception e) {
                    log.warn("get instance field {} error:{}", propDesc.getFieldName(), e.getMessage());
                }
            }
        }
    }


    /**
     * 生成实例属性更新语句
     *
     * @param param
     * @param prefix     data.标识
     * @param ignoreList 忽略字段不产生产生更新语句
     * @return
     */
    public static Update genMongoUpdateInfo(Map<String, Object> param, String prefix, List<String> ignoreList, Map<String, PropertyInfo> propertyInfoById, boolean ignoreNull) {
        Update update = new Update();
        if (null != param) {
            for (Map.Entry<String, Object> entry : param.entrySet()) {
                if (ignoreNull || entry.getValue() != null) {
                    if (ignoreList == null || (ignoreList != null && !ignoreList.contains(entry.getKey()))) {
                        String name = PropertyType.formatSearchKey(prefix, entry.getKey());
                        if (propertyInfoById != null && propertyInfoById.containsKey(entry.getKey())) {
                            PropertyInfo propertyInfo = propertyInfoById.get(entry.getKey());
                            PropertyType propertyType = PropertyTypeManage.getPropertyType(propertyInfo.getPropertyTypeRef().getTypeId());
                            update = propertyType.setUpdateValue(update, name, entry.getValue());
                        } else {
                            update.set(name, entry.getValue());
                        }
                    }
                }
            }
        }
        return update;
    }

    /**
     * 生成实例属性更新语句
     *
     * @param param
     * @param prefix     data.标识
     * @param ignoreList 忽略字段不产生产生更新语句
     * @return
     */
    public static Update genMongoUpdate(Map<String, Object> param, String prefix, List<String> ignoreList, Map<String, PropertyInfo> propertyInfoById, boolean ignoreNull) {
        return genMongoUpdateInfo(param, prefix, ignoreList, propertyInfoById, ignoreNull);
    }

    /**
     * 生成实例属性更新语句
     *
     * @param param
     * @param prefix     data.标识
     * @param ignoreList 忽略字段不产生产生更新语句
     * @return
     */
    public static Update genMongoUpdate(Map<String, Object> param, String prefix, List<String> ignoreList, Map<String, PropertyInfo> propertyInfoById) {
        return genMongoUpdateInfo(param, prefix, ignoreList, propertyInfoById, false);
    }


    public static Update genMongoModelUpdate(ModelInfo modelInfo) throws Exception {
        Update update = genMongoUpdate(modelInfo, t -> !t.getFieldName().equals(ModelInfo.PropertyMap), null);
        int updCount = 0;

        Map<String, PropertyInfo> properties = modelInfo.getProperties();
        if (null != properties) {
            String prefix = ModelInfo.PropertyMap + MongoConstant.FieldSep;
            for (Map.Entry<String, PropertyInfo> entry : properties.entrySet()) {
                PropertyInfo propertyInfo = entry.getValue();

                //不更新系统内部属性
                if (propertyInfo.isSysInternal()) {
                    continue;
                }
                String key1 = prefix + entry.getKey();
                BeanDesc desc = BeanUtil.getBeanDesc(propertyInfo.getClass());
                Iterator<PropDesc> ite = desc.getProps().iterator();
                while (ite.hasNext()) {
                    PropDesc propDesc = ite.next();
                    Field field = propDesc.getField();
                    AutoId autoId = field.getAnnotation(AutoId.class);
                    Method getMethod = propDesc.getGetter();
                    Object value = getMethod.invoke(propertyInfo);
                    if (null != value) {
                        if (null == update) {
                            update = new Update();
                        }
                        String fieldName = propDesc.getFieldName();
                        if (autoId != null && CMDBDefaultIdKey.equals(propDesc.getFieldName())) {
                            fieldName = KEY_ID;
                        }
                        String key2 = key1 + MongoConstant.FieldSep + fieldName;
                        update.set(key2, value);
                        updCount++;
                    }
                }
            }
        }

        if (updCount == 0 && null == update) {
            return null;
        }
        return update;
    }

    public static UpdateResult updateFirst(BaseMapper mapper, Query query, Update update) {
        if (null != update) {
            return mapper.updateFirst(query, update);
        }
        return null;
    }

    /*
     * logicDel: 是否逻辑删除
     */
    public static void remove(BaseMapper mapper, Object id, Class clazz, boolean logicDel) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        String idName = null;
        Update update = new Update();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            if (null != annotation && annotation.logicDel()) {
                update.set(field.getName(), true);
                //逻辑删除时，设置修改时间，修改人
                Long modifier = SecurityUtils.getUserId();
                update.set(CMDBTool.getFieldName(InstanceInfo::getModificationTime), getNowLocalTime());
                update.set(CMDBTool.getFieldName(InstanceInfo::getModifier), modifier);
            }
            AutoId autoId = field.getAnnotation(AutoId.class);
            if (null != autoId
                    || (null != annotation && annotation.id())) {
                idName = field.getName();
            }

        }

        if (null != idName) {
            Query query;
            if (id instanceof List) {
                List listIds = (List) id;
                query = new Query(Criteria.where(idName).in(listIds));
            } else {
                query = new Query(Criteria.where(idName).is(id));
            }
            if (logicDel) {
                mapper.updateMulti(query, update);
            } else {
                mapper.remove(query);
            }
        }
    }

    /*
     * 只有CMDBProperty,AutoId才处理
     */
    public static <T> void visitCMDBAnnotion(Class<T> clazz, Consumer<PropDesc> consumer) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            CMDBRelations cmdbRelations = field.getAnnotation(CMDBRelations.class);
            AutoId autoId = field.getAnnotation(AutoId.class);
            if (null != autoId || null != annotation || null != cmdbRelations) {
                consumer.accept(propDesc);
            }
        }
    }

    public static Map getFieldInfoToMap(Object obj, List<String> ignoreList) throws InvocationTargetException, IllegalAccessException {
        Map map = new HashMap();
        BeanDesc desc = BeanUtil.getBeanDesc(obj.getClass());
        Iterator<PropDesc> ite = desc.getProps().iterator();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            if (null != annotation && !ignoreList.contains(field.getName())) {
                String name = field.getName();
                Method getMethod = desc.getGetter(name);
                Object getValue = getMethod.invoke(obj);
                map.put(name, getValue);
            }
        }
        return map;
    }


    /*
     * logicDel: 逻辑删除
     */
    public static <T> List<T> logicSelectList(BaseMapper mapper, Class<T> tClass, Query query) {
        Criteria criteria = genLogicCondition(tClass);
        if (null != criteria) {
            query.addCriteria(criteria);
        }
        return mapper.find(query);
    }

    private static Criteria genLogicCondition(Class clazz) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            if (null != annotation && annotation.logicDel()) {
                return Criteria.where(field.getName()).is(false);
            }

        }
        return null;
    }


    public Criteria genLogicQueryCondition(Class clazz) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            if (null != annotation && annotation.logicDel()) {
                return new Criteria(field.getName()).is(false);
            }
        }
        return null;
    }

    public static Map<String, PropertyInfo> genSearchPropertyInfo(Class clazz) {
        return genPropertyInfo(clazz, data -> {
            PropertyInfo propertyInfo = null;
            Field field = data.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            if (null != annotation) {
                String searchType = annotation.searchType();
                if (StringUtils.hasText(searchType)) {
                    propertyInfo = new PropertyInfo();
                    propertyInfo.setId(searchType);
                    if (StringUtils.hasText(annotation.searchPrefix())) {
                        propertyInfo.setSearchPrefix(annotation.searchPrefix());
                    }
                    propertyInfo.setName(field.getName());
                    propertyInfo.setPropertyTypeRef(new PropertyTypeRef(searchType));

                    boolean keywordSearch = annotation.keywordSearch();
                    if (keywordSearch) {
                        propertyInfo.setKeywordSearch(true);
                    }
                }
            }
            return propertyInfo;
        });
    }

    private static Map<String, PropertyInfo> genPropertyInfo(Class clazz, Function<PropDesc, PropertyInfo> function) {
        Map<String, PropertyInfo> propertyInfos = new HashMap<>();
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            PropertyInfo propertyInfo = function.apply(propDesc);
            if (null != propertyInfo) {
                propertyInfos.put(propertyInfo.getName(), propertyInfo);
            }
        }
        return propertyInfos;
    }

    public static Query genInIdsQuery(Class clazz, List ids) {
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Iterator<PropDesc> ite = desc.getProps().iterator();
        String idName = null;
        while (ite.hasNext()) {
            PropDesc propDesc = ite.next();
            Field field = propDesc.getField();
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            AutoId autoId = field.getAnnotation(AutoId.class);

            if (null != autoId
                    || (null != annotation && annotation.id())) {
                idName = field.getName();
                break;
            }
        }
        if (StringUtils.hasText(idName)) {
            return new Query(Criteria.where(idName).in(ids));
        }
        return null;
    }

    public static <T> List<T> selectList(Class<T> clazz, BaseMapper baseMapper, Query query, SearchParam searchParam) {
        List<T> ret = null;
        if (null == query) {
            ret = baseMapper.findAll();
        } else {
            ret = baseMapper.find(query);
        }
        return ret;
    }

    public static <T> List<T> selectPageList(BaseMapper baseMapper, Criteria criteria, SearchParam searchParam) throws Exception {
        settingSearchSortData(searchParam);
        return MongoUtils.selectPageList(baseMapper, criteria, searchParam);
    }

    public static <T> List<T> selectAuthorizedList(Class<T> clazz, BaseMapper baseMapper, Criteria criteria, SearchParam searchParam) throws Exception {
        settingSearchSortData(searchParam);
        List<T> ret = null;
        DefaultFilter filter = DefaultFilter.build();
        if (null != criteria) {
            filter.addCondition(criteria);
        }
        if (!searchParam.isIgnoreDataAuth()) {
            QueryFilter queryFilter = new DataAuthQueryFilter(new InputDataAuth());
            filter.addDataAuth(queryFilter);
        }
        LogicDelQueryFilter logicDelQueryFilter = new LogicDelQueryFilter(clazz);
        filter.addQueryFilter(logicDelQueryFilter);

        FieldsFilter fieldsFilter = new FieldsFilter(searchParam);
        filter.addQueryFilter(fieldsFilter);
        if (null != searchParam.getPage()) {
            filter.addPage(searchParam.getPage());
            ret = baseMapper.pageFind(filter);
        } else {
            ret = baseMapper.findWithInterceptor(filter);
        }
        return ret;
    }

    private static void settingSearchSortData(SearchParam searchParam) {
        if (searchParam.getPage() != null && StringUtils.hasText(searchParam.getSortName()) && StringUtils.hasText(searchParam.getSortOrder())) {
            List<MWOrderItem> sortItems = new ArrayList<>();
            MWOrderItem mwOrderItem = new MWOrderItem();
            mwOrderItem.setColumn(searchParam.getSortName());
            mwOrderItem.setAsc(searchParam.getSortOrder().equalsIgnoreCase("ASC"));
            sortItems.add(mwOrderItem);
            searchParam.getPage().setOrders(sortItems);
        }
    }

    public static <T> List<T> aggregateAuthorizedList(Class<T> clazz, BaseMapper baseMapper, Criteria criteria, SearchParam searchParam) throws Exception {
        return aggregateAuthorizedList(clazz, clazz, baseMapper, criteria, searchParam);
    }

    /*
     * input: 获取查询的docment
     * output: 查询结果映射到output
     */
    public static <T, O> List<O> aggregateAuthorizedList(Class<T> input, Class<O> output, BaseMapper baseMapper, Criteria criteria, SearchParam searchParam) throws Exception {
        long time1 = System.currentTimeMillis();
        DefaultFilter filter = buildDefaultFilter(input, criteria, searchParam.isIgnoreDataAuth(), searchParam.isIgnoreLogicDel());
        long time2 = System.currentTimeMillis();
        if (null != output) {
            filter.setOutPutClass(output);
        }

        if (null != searchParam.getPage()) {
            settingSearchSortData(searchParam);
            filter.addAggregatePage(searchParam.getPage());
        }

        //分组统计
        if (null != searchParam.getGroupSearchOperation()) {
            filter.addAggregateQueryFilter(new GroupFilter(searchParam.getGroupSearchOperation()));
        }
        long time3 = System.currentTimeMillis();
        List list = baseMapper.aggregateWithFilter(filter);
        long time4 = System.currentTimeMillis();
        log.info("aggregateAuthorizedList耗时::buildDefaultFilter" + (time2 - time1) + "ms;组装参数:" + (time3 - time2) + "ms;aggregateWithFilter:" + (time4 - time3) + "ms");
        return list;
    }

    private static <T> DefaultFilter buildDefaultFilter(Class<T> clazz, Criteria criteria, boolean ignoreDataAuth, boolean ignoreLogicDel) {
        DefaultFilter filter = DefaultFilter.build(clazz);
        if (null != criteria) {
            filter.addAggregateCondition(criteria);
        }
        //是否忽略权限控制
        if (!ignoreDataAuth) {
            AggregateQueryFilter queryFilter = new DataAuthQueryFilter(new InputDataAuth());
            filter.addAggregateDataAuth(queryFilter);
        }

        if (!ignoreLogicDel) {
            LogicDelQueryFilter logicDelQueryFilter = new LogicDelQueryFilter(clazz);
            filter.addAggregateQueryFilter(logicDelQueryFilter);
        }

        return filter;
    }

    public static <T> int aggregateAuthorizedCount(Class<T> clazz, BaseMapper baseMapper, Criteria criteria, SearchParam searchParam) throws Exception {
        DefaultFilter filter = buildDefaultFilter(clazz, criteria, searchParam.isIgnoreDataAuth(), searchParam.isIgnoreLogicDel());
        List<AggregationOperation> aggregationOperationList = new ArrayList<>();
        List<Criteria> criteriaList = new ArrayList<>();
        Class<T> targetClass = filter.getTargetClass();
        filter.aggregateFilter(targetClass, aggregationOperationList, criteriaList, baseMapper);
        for (AggregateQueryFilter queryFilter : filter.getAggregateQueryFilters()) {
            queryFilter.aggregateFilter(targetClass, aggregationOperationList, criteriaList, baseMapper);
        }
        List<AggregationOperation> list = CMDBTool.getAggregationOperationList(targetClass, new Criteria().andOperator(criteriaList));
        list.add(Aggregation.count().as("total"));
        Aggregation aggregation = Aggregation.newAggregation(list);
        AggregationResults<Map> result = baseMapper.aggregate(aggregation, targetClass, Map.class);
        List<Map> countResult = JSONArray.parseArray(JSONArray.toJSONString(result.getMappedResults()), Map.class);
        if (!org.springframework.util.CollectionUtils.isEmpty(countResult)) {
            Map<String, Integer> countMap = countResult.get(0);
            return countMap.getOrDefault("total", 0);
        }
        return 0;
    }

    public static void cleanMongoDBClassKey(Map map, Set<Class> customClasses) {
        Iterator<Map.Entry> iterator = map.entrySet().iterator();
        Map addMap = new HashMap();
        while (iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            String key = entry.getKey().toString();
            Object value = entry.getValue();
            if (value != null && customClasses.contains(value.getClass())) {
                Map newMap = BeanUtil.beanToMap(value, false, true);
                iterator.remove();
                cleanMongoDBClassKey(newMap, customClasses);
                addMap.put(key, newMap);
            }
        }

        iterator = addMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry entry = iterator.next();
            String key = entry.getKey().toString();
            Object value = entry.getValue();
            map.put(key, value);
        }
    }

    public static long getPageStart(long pageNo, long pageSize) {
        if (pageNo < firstPageNo) {
            pageNo = firstPageNo;
        }

        if (pageSize < 1) {
            pageSize = 0;
        }

        return (pageNo - firstPageNo) * pageSize;
    }

    public static <T, R> String getFieldName(FieldGetter<T, R> func) {
        String key = func.getFieldName();
        return key;
    }

    public static <T, R> String getMongoSearchFieldName(FieldGetter<T, R> func, Class clazz) {
        String key = func.getFieldName();
        BeanDesc desc = BeanUtil.getBeanDesc(clazz);
        Field field = desc.getField(key);
        CMDBProperty cmdbProperty = field.getAnnotation(CMDBProperty.class);
        if (StringUtils.hasText(cmdbProperty.searchPrefix())) {
            return cmdbProperty.searchPrefix() + key;
        }
        return key;
    }

    public static void initCreateInfo(DataCreateInfo dataCreateInfo) {
        dataCreateInfo.setCreateTime(getNowLocalTime());
        Long creater = SecurityUtils.getUserId();
        dataCreateInfo.setCreater(creater);
    }

    public static void initCreateInfo(DataCreateInfo dataCreateInfo, Long userId) {
        dataCreateInfo.setCreateTime(getNowLocalTime());
        dataCreateInfo.setCreater(userId);
    }

    public static void initUpdateInfo(DataCreateInfo dataCreateInfo) {
        dataCreateInfo.setModificationTime(getNowLocalTime());
        Long modifier = SecurityUtils.getUserId();
        dataCreateInfo.setModifier(modifier);
    }

    public static void initUpdateInfo(DataCreateInfo dataCreateInfo, Long modifier) {
        dataCreateInfo.setModificationTime(getNowLocalTime());
        dataCreateInfo.setModifier(modifier);
    }

    public static void initCreateInfoAddtional(MwUserApi mwUserApi, DataCreateInfo src, DataCreateInfo dest) {
        List<Long> userIdList = new ArrayList<>();
        userIdList.add(src.getCreater());
        List<MwUser> mwUsers = mwUserApi.getUsersByUserIds(userIdList);
        if (null != mwUsers && mwUsers.size() > 0) {
            dest.setCreaterName(mwUsers.get(0).getUsername());
        }
    }

    public static TreeView getSubTree(String rootId, String name, List<TreeView> treeViewObjects) {
        if (null != treeViewObjects) {
            List pendingVisits = new ArrayList<>();
            for (TreeView treeView : treeViewObjects) {
                TreeViewObject treeViewObject = (TreeViewObject) treeView;
                if ((StringUtils.hasText(rootId) && treeViewObject.getId().equals(rootId))
                        || (StringUtils.hasText(name) && treeViewObject.getItemName().equals(name))) {
                    return treeViewObject;
                } else {
                    pendingVisits.add(treeViewObject);
                }
            }
            Iterator ite = pendingVisits.iterator();
            while (ite.hasNext()) {
                TreeViewObject treeViewObject = (TreeViewObject) ite.next();
                TreeView treeView = getSubTree(rootId, name, treeViewObject.getChilds());
                ite.remove();
                if (null != treeView) {
                    return treeView;
                }
            }
        }
        return null;
    }

    public static <T> List<T> selectCountByAggregate(Class<T> clazz, BaseMapper baseMapper, Criteria criteria, GroupSearchOperation groupSearchOperation) throws Exception {
        List<AggregationOperation> aggregationOperationList = getAggregationOperationList(clazz, criteria);
        //分组统计
        if (null != groupSearchOperation) {
            new GroupFilter(groupSearchOperation).aggregateFilter(clazz, aggregationOperationList, null, baseMapper);
        }
        Aggregation aggregation = Aggregation.newAggregation(aggregationOperationList);
        AggregationResults<Map> result = baseMapper.aggregate(aggregation, clazz, Map.class);
        return JSONArray.parseArray(JSONArray.toJSONString(result.getMappedResults()), clazz);
    }

    public static <T> List<T> selectListByAggregate(Class<T> clazz, BaseMapper baseMapper, Criteria criteria) {
        List<AggregationOperation> aggregationOperationList = getAggregationOperationList(clazz, criteria);
        Aggregation aggregation = Aggregation.newAggregation(aggregationOperationList);
        AggregationResults<Map> result = baseMapper.aggregate(aggregation, clazz, Map.class);
        return JSONArray.parseArray(JSONArray.toJSONString(result.getMappedResults()), clazz);
    }

    public static <T> List<T> selectPageListByAggregate(Class<T> clazz, BaseMapper baseMapper, SearchParam searchParam, Criteria criteria) {
        List<AggregationOperation> aggregationOperationList = getAggregationOperationList(clazz, criteria);
        MWPage mwPage = searchParam.getPage();
        List<AggregationOperation> countOperationList = new ArrayList<>(aggregationOperationList);
        countOperationList.add(Aggregation.count().as("total"));
        Aggregation countAggregation = Aggregation.newAggregation(countOperationList);
        AggregationResults<Map> countResult = baseMapper.aggregate(countAggregation, clazz, Map.class);
        List<Map> countResultList = JSONArray.parseArray(JSONArray.toJSONString(countResult.getMappedResults()), Map.class);
        if (!org.springframework.util.CollectionUtils.isEmpty(countResultList)) {
            Map<String, Integer> countMap = countResultList.get(0);
            mwPage.setTotal(countMap.getOrDefault("total", 0));
        }

        if (searchParam.getPage() != null && StringUtils.hasText(searchParam.getSortName()) && StringUtils.hasText(searchParam.getSortOrder())) {
            AggregationOperation sort = Aggregation.sort(Sort.by(searchParam.getSortOrder().equalsIgnoreCase("ASC") ? Sort.Direction.ASC : Sort.Direction.DESC, searchParam.getSortName()));
            aggregationOperationList.add(sort);
        }

        AggregationOperation skip = Aggregation.skip((mwPage.getCurrent() - 1) * mwPage.getSize());
        AggregationOperation limit = Aggregation.limit(mwPage.getSize());

        aggregationOperationList.add(skip);
        aggregationOperationList.add(limit);

        Aggregation aggregation = Aggregation.newAggregation(aggregationOperationList);
        AggregationResults<Map> result = baseMapper.aggregate(aggregation, clazz, Map.class);
        return JSONArray.parseArray(JSONArray.toJSONString(result.getMappedResults()), clazz);
    }

    public static List<AggregationOperation> getAggregationOperationList(Class<?> clazz, Criteria criteria) {
        return MongoUtils.getAggregationOperationList(clazz, criteria);
    }

    private static String getDbTableNameByClass(Class<?> clazz) {
        org.springframework.data.mongodb.core.mapping.Document document = clazz.getAnnotation(org.springframework.data.mongodb.core.mapping.Document.class);
        if (null == document) {
            Class<?> parentClass = clazz.getSuperclass();
            document = parentClass.getAnnotation(org.springframework.data.mongodb.core.mapping.Document.class);
        }
        return document.value();
    }

    private static List<Field> getAllFields(Class<?> clazz) {
        List<Field> fields = new ArrayList<>(Arrays.asList(clazz.getDeclaredFields()));
        // 获取父类的字段
        if (clazz.getSuperclass() != null) {
            fields.addAll(getAllFields(clazz.getSuperclass()));
        }
        return fields;
    }

    // 过滤空数据
    public static InstanceInfo filterEmptyData(InstanceInfo obj) throws IllegalAccessException {
        InstanceInfo filteredObj = new InstanceInfo();
        if (obj == null) {
            return filteredObj;
        }
        Field[] fields = obj.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            Object value = field.get(obj);
            if (value == null) {
                continue;
            }
            CMDBProperty annotation = field.getAnnotation(CMDBProperty.class);
            if (annotation != null) {
                field.set(filteredObj, value);
            }
        }
        return filteredObj;
    }

    // 根据字段名获取@Schema(description = "字段名")里面的description的值
    public static String getDescriptionFromSchema(Field field) {
        Annotation[] annotations = field.getDeclaredAnnotations();
        for (Annotation annotation : annotations) {
            if (annotation instanceof Schema) {
                return ((Schema) annotation).description();
            }
        }
        return null;
    }


    public static List<String> stringToList(String str) {
        List<String> integers = new ArrayList<>();
        if (str != null) {
            str = str.replace("[", "");
            str = str.replace("\"", "");
            str = str.replace("]", "");
            String[] s = str.split(",");
            for (String p : s) {
                if (!p.trim().equals("")) {
                    integers.add(p.trim());
                }

            }
        }
        return integers;
    }

    public static List<List<Long>> stringToListList(String str) {
        List<List<Long>> list = new ArrayList<>();
        String[] parts = str.substring(2, str.length() - 2).split("],\\[");
        for (String part : parts) {
            list.add(parseStringToList("[" + part + "]"));
        }
        return list;
    }

    public static List<Long> parseStringToList(String input) {
        List<Long> list = new ArrayList<>();
        // 移除字符串中的首尾括号，然后使用逗号分割字符串
        String[] parts = input.substring(1, input.length() - 1).split(",");
        for (String part : parts) {
            // 将分割出的字符串转换为整数并添加到列表中
            list.add(Long.parseLong(part.trim()));
        }
        return list;
    }

    /**
     * 加密字段处理(转为全是*的字段，前端显示)
     */
    public static void encryptValueHandle(Map map) {
        if (map != null) {
            map.forEach((k, v) -> {
                if (encryptFieldList.contains(k)) {
                    v = "******";
                    map.put(k, v);
                }
            });
        }
    }

    /**
     * 列表展示时机柜状态处理
     */
    public static void updateCabinetStateByMap(Object objMap) throws Exception {
        Map ms = new HashMap<>();
        if (objMap instanceof Map) {
            ms = (Map) objMap;
        }
        if (ms.get(cabinetCode) != null && ms.get(cabinetRuleType) != null) {
            List<String> cabinetCodes = getRelationCabinetCodes();
            if (cabinetCodes != null && cabinetCodes.contains(strValueConvert(ms.get(cabinetCode)))) {
                ms.put(onlineStateKey, online.getName());
            } else {
                ms.put(onlineStateKey, offline.getName());
            }
        }
    }


    /**
     * 列表展示时机柜状态处理
     */
    public static void updateCabinetStateByList(Object objList) throws Exception {
        List<Map> listViewObject = new ArrayList<>();
        if (objList instanceof MWPage) {
            MWPage pageList = (MWPage) objList;
            listViewObject = pageList.getRecords();
        } else {
            listViewObject = (List<Map>) objList;
        }
        boolean isCabinetDevice = false;
        for (Map<String, Object> ms : listViewObject) {
            if (ms.get(cabinetCode) != null && ms.get(cabinetRuleType) != null) {
                isCabinetDevice = true;
            }
        }
        List<String> cabinetCodes = new ArrayList<>();
        if (isCabinetDevice) {
            cabinetCodes = getRelationCabinetCodes();
        }
        for (Map ms : listViewObject) {
            if (ms.get(cabinetCode) != null && ms.get(cabinetRuleType) != null) {
                if (cabinetCodes != null && cabinetCodes.contains(strValueConvert(ms.get(cabinetCode)))) {
                    ms.put(onlineStateKey, online.getName());
                } else {
                    ms.put(onlineStateKey, offline.getName());
                }
            }

        }
    }


    private static List<String> getRelationCabinetCodes() throws Exception {
        RedisUtils redisUtil = SpringUtils.getBean(RedisUtils.class);
        CmdbServiceManage cmdbServiceManage = SpringUtils.getBean(CmdbServiceManage.class);
        List<String> cabinetCodes = new ArrayList<>();
        String redisKey = redisUMangeKey + "*";
        Object dataInfo = redisUtil.keys(redisKey);
        if (dataInfo != null && dataInfo instanceof Set<?>) {
            Set<String> dataInfoKeys = (Set<String>) dataInfo;
            Set dataInfoSet = new HashSet();
            for (String key : dataInfoKeys) {
                dataInfoSet = (Set) redisUtil.get(key);
            }
            Query query = new Query(CriteriaWrapper.where(V6800InitDataInfo::getData).elemMatch(CriteriaWrapper.where(UManageInitDataCabinetPortInfo::getModuleSn).in(dataInfoSet)));
            UNumManageServiceImpl uNumManageService = (UNumManageServiceImpl) cmdbServiceManage.getModelService(UNumManageServiceImpl.ID);
            List<V6800InitDataInfo> initDatas = uNumManageService.getInitDataInfo(query);
            if (org.apache.commons.collections.CollectionUtils.isNotEmpty(initDatas)) {
                for (V6800InitDataInfo initDataInfo : initDatas) {
                    if (initDataInfo != null && !Strings.isNullOrEmpty(initDataInfo.getGatewaySn()) && org.apache.commons.collections.CollectionUtils.isNotEmpty(initDataInfo.getData())) {
                        List<UManageInitDataCabinetPortInfo> data = initDataInfo.getData();
                        for (UManageInitDataCabinetPortInfo initDataCabinet : data) {
                            String moduleSn = initDataCabinet.getModuleSn();
                            if (dataInfoSet != null && dataInfoSet.contains(moduleSn)) {
                                cabinetCodes.add(initDataCabinet.getCabinetCode());
                            }
                        }
                    }
                }
            }
        }
        return cabinetCodes;
    }

}
